#pragma once

#include "../../inc/TopoDS/OMIT_MeshForwardDecl.h"
#include "../../inc/core/OMIT_RandAccessSet.h"
#include "../../inc/core/OMIT_MacroDefines.h"
#include "OMIT_MeshUserDef.h"

#include <memory_resource>

namespace OMIT_NAMESPACE
{
	// 使用案例
	// // 前向声明
	// class V;
	// class E;
	// class P;
	// class H;
	// using Traits_VEPH = HEMeshTraits<V, E, P, H>;
	// // 定制顶点、边、面和半边类型（半边类型不建议定制）
	// class V : public TVertex  <Traits_VEPH> { /*...*/ }
	// class E : public TEdge    <Traits_VEPH> { /*...*/ }
	// class P : public TFace    <Traits_VEPH> { /*...*/ }
	// class H : public THalfEdge<Traits_VEPH> { /*...*/ }
	// ...
    // // 构造一个边数为 3，具有四个顶点（0，1，2，3）
    // // 具有两个面 {0, 1, 2} 和 {0, 2, 3} 的网格
	// HEMesh<Traits_VEPH> mesh({0,1,2,0,2,3}, 3);
	template<typename Traits = MeshTraits_DefaultVEFH>
	class TMesh {
		static_assert(Traits::IsValid());

	public:
		using TV = MeshTraits_V<Traits>;
		using TE = MeshTraits_E<Traits>;
		using TF = MeshTraits_F<Traits>;
		using TH = MeshTraits_H<Traits>;

		///////////////////
		//    构造函数    //
		///////////////////

		TMesh() = default;
		TMesh(const std::vector<std::vector<size_t>>& faces) { Init(faces); }
		TMesh(const std::vector<size_t>& faces, size_t sides) { Init(faces, sides); }

        // 最小索引从 0 起初始化网格，例如：
        // faces: {
        //      { 0, 1, 2 }
        //      { 3, 4, 5, 6 }
        // }
		bool Init(const std::vector<std::vector<size_t>>& faces);
        
        // 最小索引从 0 起初始化网格，指定边数为 sides，例如：
		// faces: { 0,1,3,2,3,1 }
		// sides : 3
		bool Init(const std::vector<size_t>& faces, size_t sides);

        // 以顶点索引的形式导出所有多边形的顶点
		std::vector<std::vector<size_t>> Export() const;

        // （！耗时）网格合法的判据为：
        //  - 每个半边都有合法的后继（next），对偶（pair），起点（origin）和边（edge）
        //  - 所有的半边都来自某个圈（多边形面的半边构成的圈）
        //  - 每个半边的对偶再对偶是自己
        //  - 从每个顶点指出的半边，其起点对应这个顶点
        //  - 每个多边形都关联到一些半边，且这些半边的多边形面都是它自己
		bool IsValid() const;

		// 网格是合法的（IsValid()）并且每个多边形的面的度都为 3
        bool IsTriMesh() const;

		///////////////////
		//    成员访问    //
		///////////////////

		const std::vector<TV*>& Vertices()  noexcept { return _verts.vec(); }
		const std::vector<TE*>& Edges()     noexcept { return _edges.vec(); }
		const std::vector<TF*>& Faces()     noexcept { return _faces.vec(); }
		const std::vector<TH*>& HalfEdges() noexcept { return _halfEdges.vec(); }

        // 返回值数组中的每个半边都是位于边界上的半边
        // 可以使用 THalfEdge::NextLoop() 获取位于边界的所有半边
		std::vector<TH*> Boundaries() const;

        // 判断是否存在边界，比 Boundaries().size() > 0 更快
		bool HasBoundary() const noexcept;

		///////////////////
		//    索引查询    //
		///////////////////
		// 
        // ！ 每个顶点、边、面和半边都具有 [0, size) 中的唯一整数索引
        // ！ 如果网格的拓扑结构改变，之前获取的整数索引会失效
        // ！ 初始化之后，顶点和多边形的整数索引与输入时相同
		// 

		size_t Index(const TV* v) const { return _verts.idx(const_cast<TV*>(v)); }
		size_t Index(const TE* e) const { return _edges.idx(const_cast<TE*>(e)); }
		size_t Index(const TF* f) const { return _faces.idx(const_cast<TF*>(f)); }
		size_t Index(const TH* h) const { return _halfEdges.idx(const_cast<TH*>(h)); }

        // 获取属于多边形面的顶点整数索引
        // ！改变拓扑结构后索引失效
		std::vector<size_t> Indices(TF* f) const;

		///////////////////
		//    容量判断    //
		///////////////////

        // 顶点数组为空，则半边数组、边数组和多边形面数组都为空
		bool IsEmpty() const noexcept { return _verts.empty(); }

		void Clear() noexcept;

        // 预留内存空间
        // - 顶点数组预留 n
        // - 面数组预留 2n
        // - 边数组预留 3n
        // - 半边数组预留 6n
		void Reserve(size_t n);

		///////////////////
		//    基本编辑    //
		///////////////////

		template<typename... Args>
		TV* AddVertex(Args&&... args) { return New<TV>(std::forward<Args>(args)...); }
		// 边 e 的半边从 v0 指向 v1
		template<typename... Args>
		TE* AddEdge(TV* v0, TV* v1, Args&&... args);
		// 多边形面的半边是 heLoop[0]
		template<typename HalfEdgeLoop, typename... Args>
		TF* AddFace(const HalfEdgeLoop& heLoop, Args&&... args);

		void RemoveFace(TF* f);
		void RemoveEdge(TE* e);
		void RemoveVertex(TV* v);

		///////////////////
		//    高级编辑    //
		///////////////////

		// 删除顶点
        // ！副作用：删除关联的多边形，添加新多边形（1-邻域多边形）
		const TF* EraseVertex(TV* v);

        // 在边 e 上添加顶点，返回顶点指针
        // 如果此边的半边从 v0 指向 v1
        // 加顶点后，此边的半边从新顶点指向 v1
		template<typename... Args>
		TV* AddEdgeVertex(TE* e, Args&&... args);

        // 在 h0 或者 h1 的多边形内，连接 h0._origin 和 h1._origin
        // ！要求 h0._face == h1._face，h0._origin != h1._origin
        // 返回边，此边关联的半边从 h0._origin 指向 h1._origin
		template<typename... Args>
		TE* ConnectVertex(TH* he0, TH* he1, Args&&... args);

        // 逆时针翻转边，保留数组中的 e，不会破坏迭代
		bool FlipEdge(TE* e);

		// 删除边
		template<typename... Args>
		TV* SplitEdge(TE* e, Args&&... args);

        // 判断边 e 能否被折叠（collapse 操作，将两个顶点合为一个）
		bool IsCollapsable(const TE* e) const;

		// 首先调用 IsCollapsable(E* e)
		template<typename... Args>
		TV* CollapseEdge(TE* e, Args&&... args);

	private:
		template<typename T> struct MemVarOf;
		template<typename T>
		friend struct MemVarOf;

        // 新建且插入数组
		template<typename T, typename... Args>
		T* New(Args&&... args);

        // 清理且删除元素
		template<typename T>
		void Delete(T* elem);

	private:
		randAccessSet<TV> _verts;
		randAccessSet<TE> _edges;
		randAccessSet<TF> _faces;
		randAccessSet<TH> _halfEdges;

		std::pmr::synchronized_pool_resource _resourcePool;

		template<typename T>
		auto& GetComponentSet() {
			if constexpr (std::is_same_v<T, TV>)
				return _verts;
			else if constexpr (std::is_same_v<T, TE>)
				return _edges;
			else if constexpr (std::is_same_v<T, TF>)
				return _faces;
			else
			{
				static_assert(std::is_same_v<T, TH>);
				return _halfEdges;
			}
		}
	};
}

#include "../../inc/TopoDS/OMIT_Mesh.inl"
